package xworker.lang.task.rule;

import org.xmeta.ActionContext;
import org.xmeta.Thing;
import xworker.lang.executor.Executor;
import xworker.lang.task.TaskManager;
import xworker.lang.util.UtilData;

import java.util.Map;
import java.util.concurrent.TimeUnit;

public class RuleTask implements Runnable{
    private static final String TAG = RuleTask.class.getName();

    Thing thing;
    ActionContext actionContext;
    boolean running = false;
    boolean stop = false;
    Map<String, Object> values;

    public RuleTask(Thing thing, ActionContext parentContext){
        this.thing = thing;
        this.actionContext = new ActionContext();
        actionContext.put("parentContext", parentContext);

        values = thing.doAction("getVariables", parentContext);
        if(values != null){
            actionContext.putAll(values);
        }
        actionContext.g().put("ruleTask", this);
    }

    public void reset(){
        ActionContext parentContext = actionContext.getObject("parentContext");

        this.actionContext = new ActionContext();
        actionContext.put("parentContext", parentContext);
        if(values != null){
            actionContext.putAll(values);
        }
        actionContext.g().put("ruleTask", this);
    }

    public Thing getThing() {
        return thing;
    }

    public ActionContext getActionContext() {
        return actionContext;
    }

    public boolean isRunning() {
        return running;
    }

    public boolean isStop() {
        return stop;
    }

    public void setStop(boolean stop) {
        this.stop = stop;
        this.running = false;
    }

    public void run(){
        check();

        if(!stop){
            long checkInterval = thing.getLong("checkInterval");
            if(checkInterval > 0){
                TaskManager.getScheduledExecutorService().schedule(this, checkInterval, TimeUnit.MILLISECONDS);
            }
        }else{
            running = false;
        }

    }

    public void start(){
        if(running){
            return;
        }

        running = true;
        stop = false;

        TaskManager.getExecutorService().submit(this);
    }

    public void stop(){
        setStop(true);
    }

    public void check(){
        for(Thing childRule : thing.getChilds("Rule")){
            if(stop){
                return;
            }

            check(childRule, true, null);
        }
    }

    public boolean isDebug(){
        return thing.getBoolean("debug");
    }

    public void check(Thing thing, boolean parentSuccess, Object parentReseult){
        try{
            if(UtilData.isTrue(thing.doAction("isActive", actionContext, "parentSucess", parentSuccess, "parentResult", parentReseult))){
                try {
                    Object object = thing.doAction("doTask", actionContext, "parentSucess", parentSuccess, "parentResult", parentReseult);
                    if(isDebug()){
                        Executor.info(TAG, "Rule has executed, rule=" + thing.getMetadata().getPath() + ", result=" + object);
                    }

                    for(Thing childRule : thing.getChilds("Rule")){
                        if(stop){
                            return;
                        }
                        check(childRule, true, object);
                    }
                }catch(Exception ee){
                    Executor.warn(TAG, "Execute rule task exception, rule=" + thing.getMetadata().getPath(), ee);
                    for(Thing childRule : thing.getChilds("Rule")){
                        check(childRule, false, ee);
                    }
                }

            }else{
                if(isDebug()){
                    Executor.info(TAG, "Rule is not active, rule=" + thing.getMetadata().getPath());
                }
            }
        }catch(Exception e){
            Executor.warn(TAG, "Check rule exception, rule=" + thing.getMetadata().getPath(), e);
        }
    }

    public static RuleTask getRuleTask(Thing thing, ActionContext actionContext){
        String key = "task";
        RuleTask task = thing.getStaticData(key);
        if(task == null){
            task = new RuleTask(thing, actionContext);
            thing.setStaticData(key, task);
        }

        return task;
    }
}
